package parse;

import java.util.*;
import Const.*;
import ExecuteEngine.Tuple;
import SystemManagement.*;

public class SFW extends SQL{
	private Attribute_list attr_list;
	private TableName_list tbl_list;
	private List<String> field_type;
	private Condition conditions;
	private Orderby order;
	
	public SFW(Attribute_list attr_l, TableName_list tbl_l, Condition conditions, Orderby o){
		this.attr_list = attr_l;
		this.tbl_list = tbl_l;
		this.conditions = conditions;
		this.order = o;
	}
	
	public Attribute_list GetAttrList(){
		return this.attr_list;
	}
	
	public TableName_list GetTblList(){
		return this.tbl_list;
	}

	public void CheckAttribute(Attribute attr, List<String> field, List<String> f_type, SM_Manager sm){
		if(attr.rel_name != null){
			int mark = 0;
			String type = null;
			for(int i=0; i<field.size(); i++){
				if(field_type.get(i).equals("N") && Tuple.Trim(field.get(i)).equals(Tuple.Trim(attr.rel_name)))
					return;
				
				if(!field.get(i).equals("SUBQUERY")&&Tuple.Trim(field.get(i)).equals(Tuple.Trim(attr.rel_name))){
					mark = 1;
					type = f_type.get(i);
					break;
				}
			}
			
			if(mark == 0)
				throw new Error("The relation name isn't in the table_list.\n");
			
			if(type.equals("T"))
				if(!sm.CheckAttribute(attr.rel_name, attr.attr_name))
					throw new Error(attr.attr_name+" does not exist");
		}
		else
		{
			int count = 0;
			for(int j=0; j<field.size(); j++){
				if(field_type.get(j).equals("N"))
					continue;
				
				if(!field.get(j).equals("SUBQUERY")&&sm.CheckAttribute(field.get(j), attr.attr_name))
					count++;
			}
			
			if(count == 0)
				throw new Error(Tuple.Trim(attr.attr_name)+": not exist\n");
			else
			if(count > 1)
				throw new Error(Tuple.Trim(attr.attr_name)+": find in two different relations. Attribute conflict\n");
		}
	}
	
	public void Check(SM_Manager sm) throws Error{
		/* Get the table list and check the subquery in the table list */
		List<String> field = new ArrayList<String>();
		field_type = new ArrayList<String>();
		
		for(int i=0; i<tbl_list.GetTNList().size(); i++){
			TableName table = tbl_list.GetTNList().get(i);
			if(table.table != null){
				try{
					table.table.Check(sm);
				}
				catch(Error e){
					throw e;
				}
				field.add(table.GetTableName());
				field_type.add("N");
			}
			else{
				int type = 0;
				try{
					type = sm.CheckTable(table.GetTableName());
				}
				catch(Error e){
					throw e;
				}
				
				field.add(table.GetTableName());
				if(type == Constant.TYPE_TABLE)
					field_type.add("T");
				else
					field_type.add("V");
			}
		}
		
		/* Check the attribute list */
		for(int i=0; i<attr_list.attributes.size(); i++){
			Attribute attr = attr_list.attributes.get(i);
			if(Tuple.Trim(attr.attr_name).equals("*"))
				continue;
			
			try{
				this.CheckAttribute(attr, field, field_type, sm);
			}
			catch(Error e){
				throw e;
			}
		}
		
		/* Check the attributes in conditions */
		if(conditions != null){
			for(int i=0; i<conditions.condition.size(); i++){
				Boolean_term term = conditions.condition.get(i);
				for(int j=0; j<term.term.size(); j++){
					Boolean_factor factor = term.term.get(j);
					
					if(factor.all != null){
						try{
							factor.all.Check(sm);
						}
						catch(Error e){
							throw e;
						}
					}
					else
					if(factor.any != null){
						try{
							factor.any.Check(sm);
						}
						catch(Error e){
							throw e;
						}
					}
					else
					if(factor.in != null){
						try{
							factor.in.Check(sm);
						}
						catch(Error e){
							throw e;
						}
					}
					else{
						if(factor.ID1 != null){
							try{
								this.CheckAttribute(factor.ID1, field, field_type, sm);
							}
							catch(Error e){
								throw e;
							}
						}
	
						if(factor.ID2 != null){
							try{
								this.CheckAttribute(factor.ID2, field, field_type, sm);
							}
							catch(Error e){
								throw e;
							}
						}
					}
				}
			}
		}
	}
	
	public LQPlan CrtAgbPlan(SM_Manager sm){
		Order or = null;
		Pi pi = null;
		Sigma sigma = null;
		Algebra algebra = null;
		LQPlan plan = null;
		
		try{
			this.Check(sm);
		}
		catch(Error e){
			throw e;
		}
//		if(this.tbl_list.GetSubquery() != null){
//			algebra = this.tbl_list.GetSubquery().CrtAgbPlan(sm).GetRoot();
//			
//			if(conditions != null){
//				sigma = new Sigma(conditions, algebra);
//				pi = new Pi(attr_list, sigma);
//			}
//			else
//				pi = new Pi(attr_list, algebra);
//			
//			if(this.order != null){
//				or = new Order(this.order, pi);
//				plan = new LQPlan(or);
//			}
//			else
//				plan = new LQPlan(pi);
//			
//			return plan;
//		}
//		
		if(conditions != null && conditions.condition.size() == 1){
			List<List<DataAttrInfo>> attr_info = new ArrayList<List<DataAttrInfo>>();
			List<DataAttrInfo> info;
			
			for(int i=0; i<tbl_list.GetTNList().size(); i++){
				info = new ArrayList<DataAttrInfo>();
				if(field_type.get(i)=="T")
					sm.GetDataAttrInfo(tbl_list.GetTNList().get(i).GetTableName(), info);
				attr_info.add(info);
			}
			
			Condition con[] = new Condition[tbl_list.GetTNList().size()+1];
			for(int i=0; i<tbl_list.GetTNList().size()+1; i++){
				con[i] = new Condition();
				con[i].condition.add(new Boolean_term());
			}
			
			List<Boolean_factor> term = conditions.condition.get(0).term;
			for(int i=0; i<term.size(); i++){
				if(term.get(i).type == 1){
					con[tbl_list.GetTNList().size()].condition.get(0).term.add(term.get(i));
					continue;
				}
				
				boolean mark = false;
				StringBuffer s = new StringBuffer(term.get(i).attrName);
				s.setLength(Constant.MAXNAME);
				String attrName = s.toString();
				
				for(int j=0; j<tbl_list.GetTNList().size(); j++){
					for(int k=0; k<attr_info.get(j).size(); k++){
						DataAttrInfo attr = attr_info.get(j).get(k);
						if(attrName.equals(attr.attrName)){
							mark = true;
							con[j].condition.get(0).term.add(term.get(i));
							break;
						}
					}
					if(mark)
						break;
				}
			}
			
			Algebra table[] = new Algebra[tbl_list.GetTNList().size()];
			for(int i=0; i<tbl_list.GetTNList().size(); i++){
				String t = this.tbl_list.GetTNList().get(i).GetTableName();
				
				if(con[i].condition.get(0).term.size() == 0){
					if(field_type.get(i).equals("T"))
						table[i] = new AlgebraTable(t.toString());
					else{
						if(tbl_list.GetTNList().get(i).table.CrtAgbPlan(sm).GetRoot() instanceof Pi){
							Pi sub = (Pi)tbl_list.GetTNList().get(i).table.CrtAgbPlan(sm).GetRoot();
							table[i] = new Pi(sub.attr_list, sub.son, tbl_list.GetTNList().get(i).GetTableName());
						}
						else{
							Order sub = (Order)tbl_list.GetTNList().get(i).table.CrtAgbPlan(sm).GetRoot();
							Pi p = (Pi)sub.son;
							table[i] = new Order(sub.order, new Pi(p.attr_list, p.son, tbl_list.GetTNList().get(i).GetTableName()));
					
						}
					}
				}
				else{
					int mark = 0;
					Index_sigma ix_s = null;
					for(int j=0; j<con[i].condition.get(0).term.size(); j++){
						Boolean_factor f = con[i].condition.get(0).term.get(j);
						if(f.attrName != null && f.all == null && f.any == null && f.in == null){
							if(sm.GetIndexNo(t.toString(), f.attrName) >= 0){
								ix_s = new Index_sigma(t, f);
								con[i].condition.get(0).term.remove(j);
								mark = 1;
								break;
							}
						}
					}
					
					if(mark == 0){
						if(tbl_list.GetTNList().get(i).table != null){
							Algebra subquery = null;
							if(tbl_list.GetTNList().get(i).table.CrtAgbPlan(sm).GetRoot() instanceof Pi){
								Pi sub = (Pi)tbl_list.GetTNList().get(i).table.CrtAgbPlan(sm).GetRoot();
								subquery = new Pi(sub.attr_list, sub.son, tbl_list.GetTNList().get(i).GetTableName());
							}
							else{
								Order sub = (Order)tbl_list.GetTNList().get(i).table.CrtAgbPlan(sm).GetRoot();
								Pi p = (Pi)sub.son;
								subquery = new Order(sub.order, new Pi(p.attr_list, p.son, tbl_list.GetTNList().get(i).GetTableName()));
						
							}
							table[i] = new Sigma(con[i], subquery);
						}
						else{
							if(field_type.get(i).equals("V")){
								Parse parse = null;
								try{
									parse = new Parse(Tuple.Trim(sm.GetViewContent(tbl_list.GetTNList().get(i).GetTableName())));
								}
								catch(Error e){
									throw e;
								}
								
								Algebra subquery = null;
								if(tbl_list.GetTNList().get(i).table.CrtAgbPlan(sm).GetRoot() instanceof Pi){
									Pi sub = (Pi)tbl_list.GetTNList().get(i).table.CrtAgbPlan(sm).GetRoot();
									subquery = new Pi(sub.attr_list, sub.son, tbl_list.GetTNList().get(i).GetTableName());
								}
								else{
									Order sub = (Order)tbl_list.GetTNList().get(i).table.CrtAgbPlan(sm).GetRoot();
									Pi p = (Pi)sub.son;
									subquery = new Order(sub.order, new Pi(p.attr_list, p.son, tbl_list.GetTNList().get(i).GetTableName()));
							
								}
								table[i] = new Sigma(con[i], subquery);
							}
							else
								table[i] = new Sigma(con[i], new AlgebraTable(t));
						}
					}
					else{
						if(con[i].condition.get(0).term.size()==0)
							table[i] = ix_s;
						else
							table[i] = new Sigma(con[i], ix_s);
					}
				}
			}
			
			if(this.tbl_list.GetTNList().size() > 1){
				Descartes m = null, n = null;
				m = new Descartes(table[0], table[1]);
				for(int i=2; i<this.tbl_list.GetTNList().size(); i++){
					n = new Descartes(m, table[i]);
					m = n;
				}
				algebra = m;
				
				if(this.tbl_list.GetNaturalJoinMark() == 1)
					algebra = new NatrualJoin(m, this.tbl_list.GetTNList());
			}
			else
				algebra = table[0];
			
			if(con[tbl_list.GetTNList().size()].condition.get(0).term.size() > 0){
				sigma = new Sigma(con[tbl_list.GetTNList().size()], algebra);
				pi = new Pi(attr_list, sigma, null);
			}
			else
				pi = new Pi(attr_list, algebra, null);
			
			if(this.order != null){
				or = new Order(this.order, pi);
				plan = new LQPlan(or);
			}
			else
				plan = new LQPlan(pi);
			
			return plan;
		}
			
		if(this.tbl_list.GetTNList().size() > 1){
			Descartes m = null, n = null;
			
			String t1 = this.tbl_list.GetTNList().get(0).GetTableName();
			String t2 = this.tbl_list.GetTNList().get(1).GetTableName();
			
			Algebra table1 = null;
			Algebra table2 = null;
			
			if(field_type.get(0).equals("V")){
				Parse parse = null;
				try{
					parse = new Parse(Tuple.Trim(sm.GetViewContent(tbl_list.GetTNList().get(0).GetTableName())));
				}
				catch(Error e){
					throw e;
				}
				
				table1 = ((CreateView)parse.absyn).content.CrtAgbPlan(sm).GetRoot();
			}

			if(field_type.get(1).equals("V")){
				Parse parse = null;
				try{
					parse = new Parse(Tuple.Trim(sm.GetViewContent(tbl_list.GetTNList().get(1).GetTableName())));
				}
				catch(Error e){
					throw e;
				}
				
				table1 = ((CreateView)parse.absyn).content.CrtAgbPlan(sm).GetRoot();
			}

			if(tbl_list.GetTNList().get(0).table != null){
				if(tbl_list.GetTNList().get(0).table.CrtAgbPlan(sm).GetRoot() instanceof Pi){
					Pi sub = (Pi)tbl_list.GetTNList().get(0).table.CrtAgbPlan(sm).GetRoot();
					table1 = new Pi(sub.attr_list, sub.son, tbl_list.GetTNList().get(0).GetTableName());
				}
				else{
					Order sub = (Order)tbl_list.GetTNList().get(0).table.CrtAgbPlan(sm).GetRoot();
					Pi p = (Pi)sub.son;
					table1 = new Order(sub.order, new Pi(p.attr_list, p.son, tbl_list.GetTNList().get(0).GetTableName()));
				}
			}

			if(tbl_list.GetTNList().get(1).table != null){
				if(tbl_list.GetTNList().get(1).table.CrtAgbPlan(sm).GetRoot() instanceof Pi){
					Pi sub = (Pi)tbl_list.GetTNList().get(1).table.CrtAgbPlan(sm).GetRoot();
					table2 = new Pi(sub.attr_list, sub.son, tbl_list.GetTNList().get(1).GetTableName());
				}
				else{
					Order sub = (Order)tbl_list.GetTNList().get(1).table.CrtAgbPlan(sm).GetRoot();
					Pi p = (Pi)sub.son;
					table2 = new Order(sub.order, new Pi(p.attr_list, p.son, tbl_list.GetTNList().get(1).GetTableName()));
			
				}
			}

			if(tbl_list.GetTNList().get(0).table == null && field_type.get(0).equals("T"))
				table1 = new AlgebraTable(t1);

			if(tbl_list.GetTNList().get(1).table == null && field_type.get(1).equals("T"))
				table2 = new AlgebraTable(t2);


			m = new Descartes(table1, table2);
			
			
			for(int i=2; i<this.tbl_list.GetTNList().size(); i++){
				t1 = this.tbl_list.GetTNList().get(i).GetTableName();
				
				if(tbl_list.GetTNList().get(i).table == null)
					n = new Descartes(m, new AlgebraTable(t1));
				else
				if(field_type.get(i).equals("V")){
					Parse parse = null;
					try{
						parse = new Parse(Tuple.Trim(sm.GetViewContent(tbl_list.GetTNList().get(i).GetTableName())));
					}
					catch(Error e){
						throw e;
					}
					
					n = new Descartes(m, ((CreateView)parse.absyn).content.CrtAgbPlan(sm).GetRoot());
				}
				else{
					Algebra subquery = null;
					if(tbl_list.GetTNList().get(i).table.CrtAgbPlan(sm).GetRoot() instanceof Pi){
						Pi sub = (Pi)tbl_list.GetTNList().get(i).table.CrtAgbPlan(sm).GetRoot();
						subquery = new Pi(sub.attr_list, sub.son, tbl_list.GetTNList().get(i).GetTableName());
					}
					else{
						Order sub = (Order)tbl_list.GetTNList().get(i).table.CrtAgbPlan(sm).GetRoot();
						Pi p = (Pi)sub.son;
						subquery = new Order(sub.order, new Pi(p.attr_list, p.son, tbl_list.GetTNList().get(i).GetTableName()));
				
					}
					n = new Descartes(m, subquery);
				}
				
				m = n;
			}
			algebra = m;
			
			if(this.tbl_list.GetNaturalJoinMark() == 1)
				algebra = new NatrualJoin(m, this.tbl_list.GetTNList());
		}
		else{
			if(field_type.get(0) == "T" && tbl_list.GetTNList().get(0).table == null)
				algebra = new AlgebraTable(this.tbl_list.GetTNList().get(0).GetTableName());
			else
			if(tbl_list.GetTNList().get(0).table != null){
				Algebra subquery = null;
				if(tbl_list.GetTNList().get(0).table.CrtAgbPlan(sm).GetRoot() instanceof Pi){
					Pi sub = (Pi)tbl_list.GetTNList().get(0).table.CrtAgbPlan(sm).GetRoot();
					subquery = new Pi(sub.attr_list, sub.son, tbl_list.GetTNList().get(0).GetTableName());
				}
				else{
					Order sub = (Order)tbl_list.GetTNList().get(0).table.CrtAgbPlan(sm).GetRoot();
					Pi p = (Pi)sub.son;
					subquery = new Order(sub.order, new Pi(p.attr_list, p.son, tbl_list.GetTNList().get(0).GetTableName()));
			
				}
				algebra = subquery;
			}
			else{
				Parse parse = null;
				try{
					parse = new Parse(Tuple.Trim(sm.GetViewContent(tbl_list.GetTNList().get(0).GetTableName())));
				}
				catch(Error e){
					throw e;
				}
				
				algebra = ((CreateView)parse.absyn).content.CrtAgbPlan(sm).GetRoot();
			}
		}
		
		if(conditions != null && conditions.condition.size() > 1){
			sigma = new Sigma(conditions, algebra);
			pi = new Pi(attr_list, sigma, null);
		}
		else
			pi = new Pi(attr_list, algebra, null);
		
		if(this.order != null){
			or = new Order(this.order, pi);
			plan = new LQPlan(or);
		}
		else
			plan = new LQPlan(pi);
		
		return plan;
	}
}